/*
 *  Grammar.h
 *  NLParserDemo
 *
 *  Created by PH on 16.9.10.
 *  Copyright 2010 Petr Homola. All rights reserved.
 *
 */

#ifndef NLP_GRAMMAR_H_
#define NLP_GRAMMAR_H_

#include "global.h"
#include <string>
#include <iostream>
#include <vector>
#include "Rule.h"
#include "PhraseTable.h"
#include "ListParser.h"

namespace nlp {

	using namespace std;
#ifdef TR1
	using namespace std::tr1;
#endif
	
	class Grammar {
		
		private:
		vector<Rule*> rules;
		
		bool apply(int level, PhraseTable* chart) {
			__block bool applied = false;
			__block Phrase *phrase1, *phrase2, *phrase3;
			__block Rule* rule;
			__block vector<Rule*>::const_iterator it;
			__block AVM* avm;
			__block Phrase* newPhrase;
			//__block vector<Phrase*>* phrases;
			__block vector<Phrase*>::const_iterator it2;
			__block bool unique;
			vector<Phrase*>* newPhrases = new vector<Phrase*>;
			chart->enumerateSubchains(1, BLOCK(&)(vector<Phrase*>* phrases) {
				phrase1 = (*phrases)[0];
				if (phrase1->getLevel() == level) {
					for (it = rules.begin(); it != rules.end(); ++it) {
						rule = *it;
						avm = rule->apply(phrases);
						if (avm != NULL) {
							phrase1->setUsed(true);
							unique = true;
							phrases = chart->phrasesBeginningAt(phrase1->getBegin());
							cout << "########## checking " << phrases->size() << " phrases" << endl;
							for (it2 = phrases->begin(); it2 != phrases->end(); ++it2) {
								if (phrase1->getEnd() == (*it2)->getEnd()) {
									if (avm->equals((*it2)->getAvm())) {
										unique = false;
										delete avm;
										break;
									}
								}
							}
							if (unique) {
								newPhrase = new Phrase(shared_ptr<AVM>(avm), phrase1->getBegin(), phrase1->getEnd());
								newPhrase->setLevel(level + 1);
								newPhrases->push_back(newPhrase);
								applied = true;
								cout << "# Rule applied (" << phrase1->getBegin() << "-" << phrase1->getEnd() << "/" << level << ")" << endl;
							} else cout << "########## Skipping identical AVM" << endl;
						}
					}
				}
			});
			chart->enumerateSubchains(2, BLOCK(&)(vector<Phrase*>* phrases) {
				phrase1 = (*phrases)[0];
				phrase2 = (*phrases)[1];
				if ((phrase1->getLevel() == level && phrase2->getLevel() <= level)
					|| (phrase1->getLevel() <= level && phrase2->getLevel() == level)) {
					for (it = rules.begin(); it != rules.end(); ++it) {
						rule = *it;
						avm = rule->apply(phrases);
						if (avm != NULL) {
							phrase1->setUsed(true);
							phrase2->setUsed(true);
							unique = true;
							phrases = chart->phrasesBeginningAt(phrase1->getBegin());
							cout << "########## checking " << phrases->size() << " phrases" << endl;
							for (it2 = phrases->begin(); it2 != phrases->end(); ++it2) {
								if (phrase2->getEnd() == (*it2)->getEnd()) {
									if (avm->equals((*it2)->getAvm())) {
										unique = false;
										delete avm;
										break;
									}
								}
							}
							if (unique) {
								newPhrase = new Phrase(shared_ptr<AVM>(avm), phrase1->getBegin(), phrase2->getEnd());
								newPhrase->setLevel(level + 1);
								newPhrases->push_back(newPhrase);
								applied = true;
								cout << "# Rule applied (" << phrase1->getBegin() << "-" << phrase2->getEnd() << "/" << level << ")" << endl;
							} else cout << "########## Skipping identical AVM" << endl;
						}
					}
				}
			});
			chart->enumerateSubchains(3, BLOCK(&)(vector<Phrase*>* phrases) {
				phrase1 = (*phrases)[0];
				phrase2 = (*phrases)[1];
				phrase3 = (*phrases)[2];
				if (phrase1->getLevel() <= level && phrase2->getLevel() <= level && phrase3->getLevel() <= level
					&& (phrase1->getLevel() == level || phrase2->getLevel() == level || phrase3->getLevel() == level)) {
					for (it = rules.begin(); it != rules.end(); ++it) {
						rule = *it;
						avm = rule->apply(phrases);
						if (avm != NULL) {
							phrase1->setUsed(true);
							phrase2->setUsed(true);
							phrase3->setUsed(true);
							unique = true;
							phrases = chart->phrasesBeginningAt(phrase1->getBegin());
							cout << "########## checking " << phrases->size() << " phrases" << endl;
							for (it2 = phrases->begin(); it2 != phrases->end(); ++it2) {
								if (phrase3->getEnd() == (*it2)->getEnd()) {
									if (avm->equals((*it2)->getAvm())) {
										unique = false;
										delete avm;
										break;
									}
								}
							}
							if (unique) {
								newPhrase = new Phrase(shared_ptr<AVM>(avm), phrase1->getBegin(), phrase3->getEnd());
								newPhrase->setLevel(level + 1);
								newPhrases->push_back(newPhrase);
								applied = true;
								cout << "# Rule applied (" << phrase1->getBegin() << "-" << phrase3->getEnd() << "/" << level << ")" << endl;
							} else cout << "########## Skipping identical AVM" << endl;
						}
					}
				}
			});
			for (vector<Phrase*>::const_iterator it = newPhrases->begin(); it != newPhrases->end(); ++it) {
				chart->add(*it);
			}
			delete newPhrases;
			return applied;
		}
		
		public:
        Grammar() {}
        Grammar(const vector<ListElement>& rules) {
            for (const auto& rule : rules) {
                if (rule.getText() == nullptr && rule.getList().size() > 0 && rule.getList()[0].getText() != nullptr
                    && *(rule.getList()[0].getText()) == "#") { /*cout << "skipping comment" << endl;*/ continue; }
                add(new Rule(rule));
            }
        }
		~Grammar() {
			for (vector<Rule*>::const_iterator it = rules.begin(); it != rules.end(); ++it) {
				delete *it;
			}
		}
		
		int apply(PhraseTable* chart) {
			int level = 0;
			while (apply(level, chart)) level++;
			return level;
		}
		
		void add(Rule* rule) {
			rules.push_back(rule);
		}
		
	};

}

#endif
